#include "ObjectManager.h"

#include "Physics.h"
#include "GameObject.h"
#include "GamePlayState.h"

ObjectManager* ObjectManager::pInstance = nullptr;

/*static*/ ObjectManager* ObjectManager::GetInstance( void )
{
	// Allocate the static pointer if necessary
	if( pInstance == nullptr )
		pInstance = new ObjectManager;

	// Return the singleton
	return pInstance;
}

/*static*/ void ObjectManager::DeleteInstance( void )
{
	delete pInstance;
	pInstance = nullptr;
}

ObjectManager::ObjectManager(void)
{

}


ObjectManager::~ObjectManager(void)
{
	RemoveAll();
}


void ObjectManager::UpdateAll(float deltaTime)
{
	//Loop through each bucket
	for(unsigned int curBucket = 0; curBucket < objectTable.size(); ++curBucket)
	{
		//loop through each GameObject in each bucket
		for(list<GameObject*>::iterator curElement = objectTable[curBucket].begin(); curElement != objectTable[curBucket].end(); ++curElement )
		{
			(*curElement)->Update(deltaTime);
		}
	}
}
void ObjectManager::RenderAll() const
{
	//Loop through each bucket
	for(unsigned int curBucket = 0; curBucket < objectTable.size(); ++curBucket)
	{
		//loop through each GameObject in each bucket
		for(list<GameObject*>::const_iterator curElement = objectTable[curBucket].begin(); curElement != objectTable[curBucket].end(); ++curElement )
		{
			(*curElement)->Render();
		}
	}
}

//Check collisions for all objects in the game, skipping over tiles/background
void ObjectManager::CheckAllCollisions()
{
	//TODO: Skip/stop before non-collision layers

	//loop through each GameObject in Static objects bucket
	for(list<GameObject*>::iterator curElement = objectTable[STATIC_OBJECTS].begin(); curElement != objectTable[STATIC_OBJECTS].end(); ++curElement )
	{
		//Loop through each bucket, checking against all it can collide with 
		for(unsigned int collisionBucket = GAMEOBJECTS; collisionBucket < objectTable.size(); ++collisionBucket)
		{
			//skip objects that don't collide
			if(collisionBucket == NOCOLLISION)
				continue;
			CheckCollision(**curElement, collisionBucket);
		}
	}
	
	//loop through each GameObject in Player Bucket
	for(list<GameObject*>::iterator curElement = objectTable[PLAYER].begin(); curElement != objectTable[PLAYER].end(); ++curElement )
	{
		//Loop through each bucket, checking against all it can collide with 
		for(unsigned int collisionBucket = GAMEOBJECTS; collisionBucket < objectTable.size(); ++collisionBucket)
		{
			//skip objects that don't collide
			if(collisionBucket == NOCOLLISION)
				continue;
			//skip player - don't collide against self
			if(collisionBucket == PLAYER)
				continue;
			CheckCollision(**curElement, collisionBucket);
		}
	}

	//loop through each GameObject in GameObject bucket
	for(list<GameObject*>::iterator curElement = objectTable[GAMEOBJECTS].begin(); curElement != objectTable[GAMEOBJECTS].end(); ++curElement )
	{
		//Loop through each bucket, checking against all it can collide with 
		for(unsigned int collisionBucket = GAMEOBJECTS; collisionBucket < objectTable.size(); ++collisionBucket)
		{
			//skip objects that don't collide
			if(collisionBucket == NOCOLLISION)
				continue;
			if(collisionBucket == PLAYER)
				continue;
			CheckCollision(**curElement, collisionBucket);
		}
	}
	
}

//Check for collision against all objects in a particular bucket
void ObjectManager::CheckCollision(GameObject& objectToCheck, int bucket)
{
	for(list<GameObject*>::iterator curElement = objectTable[bucket].begin(); curElement != objectTable[bucket].end(); ++curElement)
	{
		CheckCollision(objectToCheck, **curElement);
	}
}

//Check for collision between two gameobjects
void ObjectManager::CheckCollision(GameObject& objectToCheck, GameObject& other)
{
	//Debug player and floor
	CollisionInfo info;

	GameObject* magnetAttachedObject =  GamePlayState::GetInstance()->GetPlayer()->GetToolManager()->GetMag().GetAttched();
	if(&objectToCheck == magnetAttachedObject || &other == magnetAttachedObject)
	{
		return;
	}

	//if( objectToCheck.GetTag() == "floor" && other.GetObjectID() == "Grapple" && objectToCheck.GetHitBox().top < other.GetHitBox().bottom )
	//	int i = 0;

	//Did the objects collide?
	if(&objectToCheck != &other)
	{
		if( Physics::BoxToBox(info, objectToCheck.GetHitBox(), other.GetHitBox()) )
		{
			//Handle main object to check's collision
			objectToCheck.HandleCollision( other, info );
		}
		if( Physics::BoxToBox(info, other.GetHitBox(), objectToCheck.GetHitBox()) )
		{
			//Handle main object to check's collision
			other.HandleCollision( objectToCheck, info );
		}
	}
}

void ObjectManager::AddObject(int bucket, GameObject* objToAdd)
{
	//Expand object Table?
	if((unsigned int)bucket > objectTable.size())
	{
		objectTable.resize( bucket + 1);
	}
	if( (unsigned int)bucket> objectTable.size() - 1)
	{
		objectTable.resize( bucket + 1);
	}
	objectTable[bucket].push_back(objToAdd);
}

void ObjectManager::RemoveAll()
{
	for(unsigned int curBucket = 0; curBucket < objectTable.size(); ++curBucket)
	{
		for(list<GameObject*>::iterator curElement = objectTable[curBucket].begin(); curElement != objectTable[curBucket].end(); ++curElement )
		{
			delete *curElement;
		}
		objectTable[curBucket].clear();
	}
	objectTable.clear();
}

void ObjectManager::RemoveAllExceptPlayer()
{
	for(unsigned int curBucket = 0; curBucket < objectTable.size() - 1; ++curBucket)
	{
		for(list<GameObject*>::iterator curElement = objectTable[curBucket].begin(); curElement != objectTable[curBucket].end(); ++curElement )
		{
			if((*curElement)->GetObjectID() != "Player")
				delete *curElement;
		}
		objectTable[curBucket].clear();
	}
	//objectTable.clear();
}


const list<GameObject*> * ObjectManager::GetBucket(int bucketToGet) const
{
	if( bucketToGet > (int)objectTable.size() - 1 || bucketToGet < 0 )
		return nullptr;

	return &objectTable[bucketToGet];
}

//Store all of the checkpoint data
void ObjectManager::StoreCheckPointData()
{
	//Player Bucket - 4
	//Object Bucket - 2

	//Store the players Checkpoint Pos

	//Store the objects Checkpoint Pos
	for( list<GameObject*>::iterator curElement = objectTable[2].begin(); curElement != objectTable[2].end(); ++curElement )
	{
		(*curElement)->SetCheckPointData();
	}
}
